home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
Games
/
net3d-0.08
/
view.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-22
|
30KB
|
1,160 lines
/* view.c
*
* Functions for converting 3-d to 2-d, among other things.
*/
#include "net3d.h"
#include "icons.h"
#include "view_icon.h"
/* Stipple used for shaded objects. */
#define stipple_width 2
#define stipple_height 2
static char stipple_bits[] = {
0x01, 0x02};
/* Pixmaps for icons */
static Pixmap wall[14];
/* Global variables used for graphics */
Display *display;
Window view_win;
Pixmap view_pm;
GC view_gc;
unsigned long black,white;
Colormap view_cm;
bool wireframe=0;
int window_w=WINDOW_W;
int window_h=WINDOW_H;
static struct object *mergesort(struct object *, int);
/* information about map cache efficiency */
int mappointsdone=0;
int mapcachehits=0;
static struct point **mapcache;
static XPoint **mapcachep;
static int **mapcupd;
void init3d(struct map *mp, struct view *vw)
{
int i;
/* Create the map cache array, which contains the view-
* converted points of the map vertices.
*/
mapcache = (struct point **)calloc(mp->map_w+1,sizeof(struct point *));
for(i=0; i<=mp->map_w; i++)
mapcache[i] = (struct point *)calloc(mp->map_h+1,sizeof(struct point));
/* Create the map cache perspective array, which contains the
* perspective divided points for the map polygons.
*/
mapcachep = (XPoint **)calloc(mp->map_w+1,sizeof(XPoint *));
for(i=0; i<=mp->map_w; i++)
mapcachep[i] = (XPoint *)calloc(mp->map_h+1,sizeof(XPoint));
/* Create the map cache update array, which determines if a value
* in the map cache is up to date.
*/
mapcupd = (int **)calloc(mp->map_w+1,sizeof(int *));
for(i=0; i<=mp->map_w; i++)
mapcupd[i] = (int *)calloc(mp->map_h+1,sizeof(int));
}
/* For each object, check if it is non-moving, allowing time to be
* saved later on.
*/
void initmightsaves(struct object *opos)
{
for(; opos; opos=opos->next) {
/* mightsave is true if some time can be saved on this object,
* as it's world co-ords position doesn't change.
*/
opos->mightsave = (!opos->parent || opos->parent->type == t_scenery ||
opos->parent->type == t_static || opos->parent->type
== t_mine);
}
}
/* worldtoview -
*
* vw - view co-ordinate system to convert to
* ohead - the object list
* drive - vehicle the player is driving
* vmode - current viewing mode
*/
void worldtoview(struct view *vw, struct object *ohead, struct
vehicle *drive, int vmode)
{
float orth;
int i;
register struct object *opos;
struct point tmp;
int change=0;
struct point vo;
static int call=0;
bool mightsave;
float vwx,vwy,vwz;
/* increase the counter of the number of calls to worldtoview(). This
* is used to check the validity of values in the map cache.
*/
call++;
/* force v to be orthogonal to n */
orth = dotprod(&vw->v,&vw->n)/dotprod(&vw->n,&vw->n);
vw->v.x -= orth*vw->n.x;
vw->v.y -= orth*vw->n.y;
vw->v.z -= orth*vw->n.z;
/* create u = v x n */
vw->u.x = vw->v.y*vw->n.z - vw->v.z*vw->n.y;
vw->u.y = vw->v.z*vw->n.x - vw->v.x*vw->n.z;
vw->u.z = vw->v.x*vw->n.y - vw->v.y*vw->n.x;
/* normalise u,v and n */
normalise(&vw->u);
normalise(&vw->v);
normalise(&vw->n);
/* create view matrix */
vw->vmat[0][0]=vw->u.x;
vw->vmat[0][1]=vw->u.y;
vw->vmat[0][2]=vw->u.z;
vw->vmat[1][0]=vw->v.x;
vw->vmat[1][1]=vw->v.y;
vw->vmat[1][2]=vw->v.z;
vw->vmat[2][0]=vw->n.x;
vw->vmat[2][1]=vw->n.y;
vw->vmat[2][2]=vw->n.z;
/* check for changes */
if (chksum(vw->u) != chksum(vw->last.u)) {
vw->last.u = vw->u;
change |= ch_u;
}
if (chksum(vw->v) != chksum(vw->last.v)) {
vw->last.v = vw->v;
change |= ch_v;
}
if (chksum(vw->n) != chksum(vw->last.n)) {
vw->last.n = vw->n;
change |= ch_n;
}
if (chksum(vw->vrp) != chksum(vw->last.vrp)) {
/* don't store old vrp till later, since it might be
* needed in calculating an offset for linear motion.
*/
change |= ch_vrp;
}
/* check for a change in the view-mode */
if (vmode != vw->last.vmode) {
change |= ch_all;
vw->last.vmode = vmode;
}
/* Store the change from this call, to be used by depthsort.
*/
vw->change = change;
/* pre-calc offset from old vrp. However, this change is in world
* co-ordinates, and must be converted to view to be useful.
*/
tmp.x = vw->vrp.x - vw->last.vrp.x;
tmp.y = vw->vrp.y - vw->last.vrp.y;
tmp.z = vw->vrp.z - vw->last.vrp.z;
mmult(&tmp,vw->vmat,&vo);
for(opos=ohead; opos; opos=opos->next) {
mightsave = opos->mightsave && opos->cvalid;
/* pre-calc position of object wrt view */
vwx = opos->pos.x - vw->vrp.x;
vwy = opos->pos.y - vw->vrp.y;
vwz = opos->pos.z - vw->vrp.z;
for(i=0; i<opos->pcount; i++) {
int x,y;
register float pers,perx,pery;
float z;
if (!opos->parent) {
/* This is a ground polygon. Check the map cache
* to see if this point has already been calculated
* and perspective divided.
*/
mappointsdone++;
x = opos->mx + (i%2);
y = opos->my + (i>>1);
if (mapcupd[x][y] == call) {
/* This point has already been done! */
mapcachehits++;
opos->cpoints[i] = mapcache[x][y];
opos->ppoints[i] = mapcachep[x][y];
continue;
}
}
if (mightsave) {
/* Some time might be saved, as this object
* cannot move.
*/
if ((change & ch_uvn) && !(change & ch_vrp)) {
/* only u,v or n has changed, vrp is
* constant.
*/
tmp.x = opos->points[i].x + vwx;
tmp.y = opos->points[i].y + vwy;
tmp.z = opos->points[i].z + vwz;
if (change & ch_u || change & ch_n) {
opos->cpoints[i].x =
cmult(&tmp,vw->vmat[0]);
opos->cpoints[i].z =
cmult(&tmp,vw->vmat[2]);
}
if (change & ch_v)
opos->cpoints[i].y =
cmult(&tmp,vw->vmat[1]);
}
else if ((change & ch_vrp) && !(change & ch_uvn)) {
/* only the vrp has changed, u,v & n
* are constant. This means that the
* new positions of the points are
* linearly offset from the old.
*/
opos->cpoints[i].x -= vo.x;
opos->cpoints[i].y -= vo.y;
opos->cpoints[i].z -= vo.z;
}
else if (!(change & ch_vrp) && !(change & ch_uvn)) {
/* no change in vrp or u,v and n. Do
* nothing.
*/
}
else {
/* Both u,v or n and the vrp have changed.
* Do a total re-calc.
*/
tmp.x = opos->points[i].x + vwx;
tmp.y = opos->points[i].y + vwy;
tmp.z = opos->points[i].z + vwz;
mmult(&tmp,vw->vmat,opos->cpoints+i);
}
}
else {
/* This object moves. No chance of saving
* time.
*/
tmp.x = opos->points[i].x + vwx;
tmp.y = opos->points[i].y + vwy;
tmp.z = opos->points[i].z + vwz;
mmult(&tmp,vw->vmat,opos->cpoints+i);
}
/* do perspective division for this point */
z = opos->cpoints[i].z;
if (z < 0) {
/* If the point is behind the viewer, don't bother
* to do proper perspective division.
*/
perx = opos->cpoints[i].x*SCALE + window_w/2;
pery = -opos->cpoints[i].y*SCALE + window_h/2;
}
else {
pers = 1.0 + z/vw->d;
perx = (opos->cpoints[i].x/pers)*SCALE + window_w/2;
pery = -(opos->cpoints[i].y/pers)*SCALE + window_h/2;
}
/* allow for overflowing the 16-bit numbers in an XPoint */
if (perx > 32767.0)
opos->ppoints[i].x = 32767;
else if (perx < -32768.0)
opos->ppoints[i].x = -32768;
else
opos->ppoints[i].x = perx;
if (pery > 32767.0)
opos->ppoints[i].y = 32767;
else if (pery < -32768.0)
opos->ppoints[i].y = -32768;
else
opos->ppoints[i].y = pery;
if (!opos->parent) {
/* This is a ground polygon that has not
* been taken from the cache. Store it in
* and mark it as updated.
*/
mapcache[x][y] = opos->cpoints[i];
mapcachep[x][y] = opos->ppoints[i];
mapcupd[x][y] = call;
}
}
/* At least one conversion from world to view has been totally
* done. Mark cpoints as valid.
*/
opos->cvalid = true;
}
/* store old vrp. */
vw->last.vrp = vw->vrp;
}
/* render - draws the current 3-d view into the view window
*
* vw - current viewing position and direction
* ohead - head of the object list
* drive - vehicle currently being controlled by player
* vmode - inside, outside, lookup, etc.. view
* mp - terrain map
*/
void render(struct view *vw, struct object *ohead, struct vehicle
*drive, int vmode, struct map *mp)
{
struct object *opos; /* position in object list */
int i,j;
XPoint plotx[MAX_POINTS_PER_FACE];
struct point tmp;
long ax,ay,bx,by; /* for backface removal */
long p0x,p0y,p1x,p1y,p2x,p2y;
int rej;
float pers; /* for perspective division */
struct polygon *poly;
int *vtmp;
int pc;
int offleft, offright, offtop, offbottom;
struct point horizon;
int reallydrawn=0;
/* minimum and maximum points of the box around the vehicle the player
* has a lock on.
*/
int lxmin,lymin;
int lxmax,lymax;
bool drawlockbox=false;
lxmin=SHRT_MAX; lymin=SHRT_MAX;
lxmax=SHRT_MIN; lymax=SHRT_MIN;
XSetForeground(display,view_gc,mp->skycol);
XFillRectangle(display,view_pm,view_gc,0,0,window_w,window_h);
if (mp->stars)
drawstars(mp,vw);
/* The ground is drawn as a large, shaded rectangle instead of a
* real object in 3d.
*/
if (mp->ground) {
if (!vw->n.x && !vw->n.y && vw->n.z < 0) {
/* Allow for the situation where the player is looking
* straight down onto the ground.
*/
XSetForeground(display,view_gc,mp->gcol*32+26);
XFillRectangle(display,view_pm,view_gc,0,0,window_w,window_h);
}
else if (!vw->n.x && !vw->n.y && vw->n.z > 0) {
/* Or where the player is looking straight up into the
* sky.
*/
}
else {
horizon.x=vw->vrp.x + vw->n.x*1000000;
horizon.y=vw->vrp.y + vw->n.y*1000000;
horizon.z=0.0;
mmult(&horizon,vw->vmat,&tmp);
pers = 1.0 + tmp.z/vw->d;
tmp.y = -(tmp.y/pers)*SCALE + window_h/2;
if (wireframe) {
XSetForeground(display,view_gc,mp->gcol*32+26);
XDrawLine(display,view_pm,view_gc,0,tmp.y,window_w,
tmp.y);
}
else {
#if SHADEDGROUND
int i;
float strip;
int shade;
strip=(window_h-tmp.y)/32.0;
for(i=0; i<32; i++) {
/* adjust shade for height above ground */
shade=i-32*(vw->vrp.z/VIEW_RANGE);
XSetForeground(display,view_gc,shade<0 ?
mp->gcol*32 : mp->gcol*32+shade);
XFillRectangle(display,view_pm,view_gc,0,
tmp.y+strip*i,window_w,strip+1);
}
#else
XSetForeground(display,view_gc,95);
XFillRectangle(display,view_pm,view_gc,0,tmp.y,
window_w,window_h-tmp.y);
#endif
}
}
}
for(opos=ohead; opos; opos=opos->next) {
/* don't draw the vehicle if inside it */
if (drive && opos->parent == drive && (vmode==4 || vmode==6 ||
vmode == 8)) {
continue;
}
/* don't draw missile if inside it */
if (drive && opos->parent && opos->parent->vid == drive->missile &&
vmode==8) {
continue;
}
/* don't draw objects that are too far away, or totally behind
* the viewer.
*/
if (/* opos->cdist > VIEW_RANGE || */ opos->dist <= 0.0)
continue;
/* check for visibility, and if so draw each face */
for(i=0; i < opos->fcount; i++) {
/* quick reference to the point list for this
* object.
*/
register XPoint *currx;
currx = opos->ppoints;
rej=0;
poly=opos->faces+i;
vtmp=poly->vertices;
if (poly->type == f_face) {
/* changed to use points after perspective
* division, because perspective changes
* what can be seen.
*/
p0x=currx[vtmp[0]].x; p0y=currx[vtmp[0]].y;
p1x=currx[vtmp[1]].x; p1y=currx[vtmp[1]].y;
p2x=currx[vtmp[2]].x; p2y=currx[vtmp[2]].y;
ax=p0x-p1x; ay=p0y-p1y;
bx=p2x-p1x; by=p2y-p1y;
if (opos->clockwise) {
if (ax*by <= ay*bx)
continue;
}
else {
if (ax*by >= ay*bx)
continue;
}
}
/* store points for face in XPoint array */
pc=poly->pcount;
for(j=0;j < pc;j++)
plotx[j]=currx[vtmp[j]];
plotx[pc]=currx[vtmp[0]];
/* check if polygon is totally outside window.
* for this to be true, all points must be off to one
* side. */
offleft=offright=offtop=offbottom=0;
for(j=0;j < pc;j++) {
if (plotx[j].x < 0)
offleft++;
else if (plotx[j].x > window_w)
offright++;
if (plotx[j].y < 0)
offbottom++;
else if (plotx[j].y > window_h)
offtop++;
}
if (offleft==pc || offright==pc || offtop==pc || offbottom==pc)
rej=1;
if (!rej) {
unsigned long colbase, colshade;
/* adjust colour for distance from view */
if (poly->type != f_glass) {
colshade=poly->colour%32;
colbase=poly->colour-colshade;
if (poly->colour >= 32) {
if (opos->cdist > VIEW_RANGE)
colshade=0;
else if (opos->cdist < 0)
;
else
colshade -= (colshade*opos->
cdist/VIEW_RANGE);
}
}
/* draw face, using correct polygon style */
XSetForeground(display,view_gc,colbase+colshade);
if (wireframe) {
/* draw only outlines of polygons
* and spheres.
*/
switch(poly->type) {
case f_sphere: {
float rad;
float pers;
float z;
/* calculate on-screen radius
* of sphere.
*/
z = opos->cpoints[poly->vertices[0]].z;
pers = 1.0 + z/vw->d;
rad = (poly->radius/pers)*SCALE;
if (rad < 1) {
XDrawPoint(display,view_pm,
view_gc,plotx[0].x,
plotx[0].y);
}
else {
XDrawArc(display,view_pm,
view_gc,plotx[0].x-rad,
plotx[0].y-rad,rad*2,rad*2,
0,360*64);
}
break;
}
case f_point:
XDrawPoint(display,view_pm,view_gc,
plotx[0].x,plotx[0].y);
break;
default:
XDrawLines(display,view_pm,view_gc,
plotx,pc+1,CoordModeOrigin);
break;
}
}
else {
/* draw filled polygons correctly */
switch(poly->type) {
case f_face:
case f_plane:
XFillPolygon(display,view_pm,view_gc,
plotx,pc+1,Convex,CoordModeOrigin);
break;
case f_line:
XDrawLines(display,view_pm,view_gc,
plotx,pc,CoordModeOrigin);
break;
case f_wireframe:
XDrawLines(display,view_pm,view_gc,
plotx,pc+1,CoordModeOrigin);
break;
case f_glass:
XSetPlaneMask(display,view_gc,224L);
XSetForeground(display,view_gc,
poly->colour<<5);
XFillPolygon(display,view_pm,view_gc,
plotx,pc+1,Convex,CoordModeOrigin);
XSetPlaneMask(display,view_gc,255L);
break;
case f_sphere: {
float rad;
float pers;
float z;
/* calculate on-screen radius
* of sphere.
*/
z = opos->cpoints[poly->vertices[0]].z;
pers = 1.0 + z/vw->d;
rad = (poly->radius/pers)*SCALE;
if (rad < 1) {
XDrawPoint(display,view_pm,
view_gc,plotx[0].x,
plotx[0].y);
}
else {
XFillArc(display,view_pm,
view_gc,plotx[0].x-rad,
plotx[0].y-rad,rad*2,rad*2,
0,360*64);
}
break;
}
case f_point:
XDrawPoint(display,view_pm,view_gc,
plotx[0].x,plotx[0].y);
break;
case f_shaded:
XSetFillStyle(display,view_gc,
FillStippled);
XFillPolygon(display,view_pm,view_gc,
plotx,pc+1,Convex,CoordModeOrigin);
XSetFillStyle(display,view_gc,
FillSolid);
break;
default:
printf("unknown face type???\n");
}
}
/* if there is a lock on this object, adjust the
* lock bounding box.
*/
if (opos->parent && drive &&
opos->parent->vid == drive->lock) {
int i;
for(i=0; i<pc; i++) {
if (plotx[i].x < lxmin)
lxmin=plotx[i].x;
else if (plotx[i].x > lxmax)
lxmax=plotx[i].x;
if (plotx[i].y < lymin)
lymin=plotx[i].y;
else if (plotx[i].y > lymax)
lymax=plotx[i].y;
drawlockbox=true;
}
}
reallydrawn++;
#if DEBUG
for(j=0;j < pc;j++)
printf("%d,%d ",plotx[j].x,plotx[j].y);
putchar('\n');
#endif
}
}
}
/* draw the lock bounding box, if the vehicle is in sight */
if (drawlockbox) {
XSetForeground(display,view_gc,159);
XDrawRectangle(display,view_pm,view_gc,lxmin,lymin,
lxmax-lxmin,lymax-lymin);
}
}
/* depthsort - bubble sorts the list of objects, from furthest to
* closest.
*
* ohead - the list of objects
* vw - contains viewer's position
*/
struct object *depthsort(struct object *ohead, struct view *vw)
{
int sort; /* still sorting ? */
struct object *opos,**oprev,*temp; /* position in list */
int scount=0;
int ocount = 0; /* total number of objects */
if (!ohead)
return NULL;
/* distance to a face is the distance to it's furthest point,
* along the Z axis.
*/
for(opos=ohead; opos; opos=opos->next) {
float mxdist = -VIEW_RANGE*VIEW_RANGE*2;
float midist = VIEW_RANGE*VIEW_RANGE*2;
int i;
for(i=0; i<opos->pcount; i++) {
float dist;
dist = opos->cpoints[i].z;
if (dist > mxdist)
mxdist = dist;
if (dist < midist)
midist = dist;
}
opos->dist = mxdist;
opos->cdist = midist;
ocount++;
}
/* If the view has been rotated, then a large number of depths will have
* changed and mergesort should be used.
* If not, then the distances to objects will be largely unchanged and
* little sorting will be needed, making bubble sort an easy choice :)
*/
if (vw->change & ch_uvn) {
ohead = mergesort(ohead,ocount);
}
else {
do {
sort=0;
for(opos=ohead,oprev=&ohead; opos->next; opos=opos->next) {
if (opos->dist < opos->next->dist) {
(*oprev)=opos->next;
temp=opos->next;
opos->next=temp->next;
temp->next=opos;
opos=temp;
sort=1;
scount++;
}
oprev=&((*oprev)->next);
}
} while(sort);
}
return(ohead);
}
/* mergesort - merge sorts a list of objects.
*
* oh - the head of the object list
* oc - total objects in the list
*/
static struct object *mergesort(struct object *oh, int oc)
{
if (oc <= 1) {
/* Already sorted (of course!) */
return oh;
}
else {
int halfway;
int i;
struct object **op; /* pointer to pointer to halfway node */
struct object *bk; /* pointer to halfway node */
struct object *l1, *l2; /* sorted halves */
struct object *slist; /* final sorted list */
struct object **sl;
/* Recursively call mergesort again on the two halves of the list */
halfway = oc/2;
for(op=&oh, i=0; i < halfway; op = &((*op)->next))
i++;
bk = *op;
*op = NULL;
l1 = mergesort(oh,halfway);
l2 = mergesort(bk, oc - halfway);
/* rejoin the two halves */
sl = &slist;
while(l1 || l2) {
if (l1 != NULL && (l2 == NULL || l1->dist > l2->dist)) {
*sl = l1;
sl = &(l1->next);
l1 = l1->next;
}
else {
*sl = l2;
sl = &(l2->next);
l2 = l2->next;
}
}
*sl = NULL;
return slist;
}
}
void initX(struct map *mp, int argc, char **argv)
{
int screen_num;
Screen *screen_ptr;
static XSizeHints size_hints;
Visual *default_visual;
XColor ncol;
int i;
XEvent event;
XGCValues gcvals;
Colormap def_cm;
Pixmap view_icon;
/* Open connection to the server, and get some useful Xlib values. */
if (!(display=XOpenDisplay(NULL))) {
char *dname;
dname = XDisplayName(NULL);
printf("Error opening display %s\n",dname ? dname : "");
exit(1);
}
screen_num=DefaultScreen(display);
screen_ptr=DefaultScreenOfDisplay(display);
black=BlackPixel(display,screen_num);
white=WhitePixel(display,screen_num);
/* Create the game window */
view_win=XCreateSimpleWindow(display,RootWindow(display,screen_num),100,100,
WINDOW_W,WINDOW_H,1,black,white);
/* Create the icon pixmap */
view_icon = XCreateBitmapFromData(display,view_win,view_icon_bits,
view_icon_width,view_icon_height);
/* Set window properties and input mask */
size_hints.flags=PMinSize;
size_hints.min_width=315;
size_hints.min_height=200;
XSetStandardProperties(display,view_win,"net3d "NET3D_VERSION,"net3d",
view_icon,argv,argc,&size_hints);
XSelectInput(display,view_win,StructureNotifyMask | KeyPressMask |
ButtonPressMask);
XMapWindow(display,view_win);
/* create the pixmap for double buffering */
view_pm=XCreatePixmap(display,view_win,WINDOW_W,WINDOW_H,8);
/* create the GC used in the game */
view_gc=XCreateGC(display,RootWindow(display,screen_num),0,0);
XSetForeground(display,view_gc,white);
XFillRectangle(display,view_pm,view_gc,0,0,window_w,window_h);
/* create the stipple pixmap and set the GC to use it, as well as
* cancelling graphics_exposures on the GC.
*/
gcvals.stipple=XCreateBitmapFromData(display,view_win,stipple_bits,
stipple_width,stipple_height);
gcvals.graphics_exposures=false;
XChangeGC(display,view_gc,GCGraphicsExposures | GCStipple,&gcvals);
default_visual=DefaultVisual(display,screen_num);
view_cm=XCreateColormap(display,view_win,default_visual,AllocAll);
def_cm = DefaultColormap(display,screen_num);
/* allocate custom colourmap
* 0-31 = unchanged
* 32-63 = grey shades
* 64-95 = green shades
* 96-127 = blue shades
* 128-159 = red shades
* 160-191 = brown shades
* 192-223 = lilacs (55, 13, 47)
* 224-255 = cyans (0, 63, 63)
*/
ncol.flags = DoRed | DoGreen | DoBlue;
for(i=0; i < 32; i++) {
/* First 32 colours are from WM */
ncol.pixel=i;
XQueryColor(display,def_cm,&ncol);
XStoreColor(display,view_cm,&ncol);
/* greys */
ncol.pixel=i+32;
fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,65535,65535,i,&ncol);
XStoreColor(display,view_cm,&ncol);
/* greens */
ncol.pixel=i+64;
fadecolour(mp->rfade,mp->gfade,mp->bfade,0,65535,0,i,&ncol);
XStoreColor(display,view_cm,&ncol);
/* blues */
ncol.pixel=i+96;
fadecolour(mp->rfade,mp->gfade,mp->bfade,0,0,65535,i,&ncol);
XStoreColor(display,view_cm,&ncol);
/* reds */
ncol.pixel=i+128;
ncol.red=i<<11; ncol.green=0; ncol.blue=0;
fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,0,0,i,&ncol);
XStoreColor(display,view_cm,&ncol);
/* browns */
ncol.pixel=i+160;
ncol.red=i<<11;
ncol.green=i < 4 ? 0 : (i-4)<<11;
ncol.blue=i < 8 ? 0 : (i-8)<<11;
fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,57343,49151,i,&ncol);
XStoreColor(display,view_cm,&ncol);
/* lilacs */
ncol.pixel=i+192;
ncol.red=i<<11;
ncol.green=i<<10;
ncol.blue=i<<11;
fadecolour(mp->rfade,mp->gfade,mp->bfade,65535,32767,65535,i,&ncol);
XStoreColor(display,view_cm,&ncol);
/* cyans */
ncol.pixel=i+224;
ncol.red=0;
ncol.green=i<<11;
ncol.blue=i<<11;
fadecolour(mp->rfade,mp->gfade,mp->bfade,0,65535,65535,i,&ncol);
XStoreColor(display,view_cm,&ncol);
}
XSetWindowColormap(display,view_win,view_cm);
XFlush(display);
/* wait for the window to appear */
do {
XNextEvent(display,&event);
} while(event.type != 21);
XInstallColormap(display,view_cm);
XFlush(display);
printf("starting game\n");
}
long sqr(long x)
{
return x*x;
}
/* change colours 25-31 to a random reddish-yellow colours */
void cyclefire(double tm)
{
XColor ncol;
int i;
/* srand((((int)tm)*1000)%65536); */
for(i=25; i<32; i++) {
ncol.pixel=i;
ncol.red=65535;
ncol.green=rand()%65536;
ncol.blue=0;
ncol.flags = DoRed | DoGreen | DoBlue;
XStoreColor(display,view_cm,&ncol);
}
}
void drawextras(struct vehicle *drive, bool winner)
{
if (!drive) {
/* If there is no vehicle to drive, therefore the player
* must be dead. Tell them so :)
*/
XSetForeground(display,view_gc,63);
XDrawString(display,view_pm,view_gc,10,10,DEATH_MSG,
strlen(DEATH_MSG));
}
if (drive && winner) {
/* someone has won the game, and since this client is still
* alive, it must be us.
*/
XSetForeground(display,view_gc,63);
XDrawString(display,view_pm,view_gc,10,30,WINNER_MSG,
strlen(WINNER_MSG));
}
/* copy the completed pixmap to the front */
XCopyArea(display,view_pm,view_win,view_gc,0,0,window_w,window_h,0,0);
XFlush(display);
XSync(display,False);
}
/* draw the player's radar, gunsight, hit points, ammo, resources and lock.
*/
void drawradar(struct vehicle *vhead, struct vehicle *drive)
{
struct vehicle *v; /* vehicle up to in list */
float xd,yd,radsq; /* position of vehicle on map */
float x,y; /* position to draw the dot */
long typecols[]={191,95,85,45,52,255,159,32,159,52,255,85,45,159};
char hpbuf[20],ammobuf[20],resbuf[20]; /* strings for displaying things */
/* don't draw anything if vehicle has been destroyed */
if (!drive)
return;
/* draw radar circle */
XSetForeground(display,view_gc,32);
XFillArc(display,view_pm,view_gc,window_w-60,0,60,60,0,360*64);
for(v=vhead; v; v=v->next) {
if (v->type == t_scenery)
continue;
xd = v->parts[0]->cpoints[0].x;
yd = v->parts[0]->cpoints[0].z;
radsq = xd*xd + yd*yd;
/* check if vehicle is in range */
if (radsq < RADAR_RANGE*RADAR_RANGE) {
/* Plot the vehicle at the correct position.
*/
x = window_w-30 + (xd/RADAR_RANGE)*30;
y = 30 - (yd/RADAR_RANGE)*30;
XSetForeground(display,view_gc,typecols[v->type]);
XDrawPoint(display,view_pm,view_gc,x,y);
}
}
/* draw an outline around the radar */
XSetForeground(display,view_gc,150);
XDrawArc(display,view_pm,view_gc,window_w-60,0,60,60,0,360*64);
/* draw a gunsight */
XSetForeground(display,view_gc,63);
XDrawLine(display,view_pm,view_gc,window_w/2-10,window_h/2,window_w/2+10,
window_h/2);
XDrawLine(display,view_pm,view_gc,window_w/2,window_h/2-10,window_w/2,
window_h/2+10);
/* display hit points */
XSetForeground(display,view_gc,159);
sprintf(hpbuf,"HP %d",drive->hp);
XDrawString(display,view_pm,view_gc,window_w-60,75,hpbuf,strlen(hpbuf));
/* display ammo */
XSetForeground(display,view_gc,159);
sprintf(ammobuf,"AMMO %d",drive->ammo);
XDrawString(display,view_pm,view_gc,window_w-60,90,ammobuf,strlen(ammobuf));
/* display resources */
XSetForeground(display,view_gc,90);
sprintf(resbuf,"RES %d",drive->res);
XDrawString(display,view_pm,view_gc,window_w-60,105,resbuf,strlen(resbuf));
/* draw name info about vehicle lock is on */
XSetForeground(display,view_gc,159);
for(v=vhead; v && v->vid != drive->lock; v=v->next)
;
if (v) {
/* lock is valid. Draw the vehicle name, and name of the player
* who owns it, if any.
*/
XDrawString(display,view_pm,view_gc,10,50,v->name,strlen(v->name));
if ((v->owner == o_player || v->owner == o_network) && v->pnum != -1) {
XDrawString(display,view_pm,view_gc,10,65,pnames[v->pnum],
strlen(pnames[v->pnum]));
}
}
else {
/* cancel lock */
drive->lock = -1;
}
}
/* display the velocity, hit points and altitute of the player's
* vehicle.
*/
void drawinfo(struct vehicle *drive)
{
int vline; /* length of velocity line */
int aline; /* length of altitude line */
if (!drive)
return;
/* draw velocity bar */
XSetForeground(display,view_gc,32);
XFillRectangle(display,view_pm,view_gc,5,window_h-35,151,16);
XSetForeground(display,view_gc,125);
XDrawRectangle(display,view_pm,view_gc,5,window_h-35,151,16);
if (drive->max.velocity) {
if (drive->velocity > 0)
XSetForeground(display,view_gc,90);
else
XSetForeground(display,view_gc,145);
vline=148*(dabs(drive->velocity)/drive->max.velocity);
if (vline > 148)
vline=148;
XFillRectangle(display,view_pm,view_gc,7,window_h-33,vline,13);
}
/* draw altitude bar */
XSetForeground(display,view_gc,32);
XFillRectangle(display,view_pm,view_gc,window_w-155,window_h-35,151,16);
XSetForeground(display,view_gc,125);
XDrawRectangle(display,view_pm,view_gc,window_w-155,window_h-35,151,16);
if (drive->max.altitude) {
if (drive->parts[0]->pos.z >= 0)
XSetForeground(display,view_gc,250);
else
XSetForeground(display,view_gc,220);
aline=148*(dabs(drive->parts[0]->pos.z/drive->max.altitude));
if (aline > 148)
aline=148;
XFillRectangle(display,view_pm,view_gc,window_w-153,window_h-33,
aline,13);
}
}
void drawvmode(int vmode, struct vehicle *drive)
{
static char *vwnames[]={"Long Range View",
"Lookout View",
"External View",
"Pilot View",
"Satellite View",
"Gun View",
"Short Range View",
"Projectile View",
"Interesting Thing View"};
char vmbuf[100];
XSetForeground(display,view_gc,127);
sprintf(vmbuf,"%s : %s",drive->code,vwnames[vmode-1]);
XDrawString(display,view_pm,view_gc,10,10,vmbuf,strlen(vmbuf));
}
void fadecolour(int rst, int gst, int bst, int ren, int gen, int ben,
int x, XColor *col)
{
col->red = rst + ((ren-rst)*(x/31.0));
col->green = gst + ((gen-gst)*(x/31.0));
col->blue = bst + ((ben-bst)*(x/31.0));
}
/* Convert all the included icon files into pixmaps.
*/
void readicons(void)
{
wall[1] = XCreateBitmapFromData(display,view_win,wall1_bits,wall1_width,
wall1_height);
wall[2] = XCreateBitmapFromData(display,view_win,wall2_bits,wall2_width,
wall2_height);
wall[3] = XCreateBitmapFromData(display,view_win,wall3_bits,wall3_width,
wall3_height);
wall[4] = XCreateBitmapFromData(display,view_win,wall4_bits,wall4_width,
wall4_height);
wall[5] = XCreateBitmapFromData(display,view_win,wall5_bits,wall5_width,
wall5_height);
wall[6] = XCreateBitmapFromData(display,view_win,wall6_bits,wall6_width,
wall6_height);
wall[7] = XCreateBitmapFromData(display,view_win,wall7_bits,wall7_width,
wall7_height);
wall[8] = XCreateBitmapFromData(display,view_win,wall8_bits,wall8_width,
wall8_height);
wall[9] = XCreateBitmapFromData(display,view_win,wall9_bits,wall9_width,
wall9_height);
wall[10]= XCreateBitmapFromData(display,view_win,wall10_bits,wall10_width,
wall10_height);
wall[11]= XCreateBitmapFromData(display,view_win,wall11_bits,wall11_width,
wall11_height);
wall[12]= XCreateBitmapFromData(display,view_win,wall12_bits,wall12_width,
wall12_height);
wall[13]= XCreateBitmapFromData(display,view_win,wall13_bits,wall13_width,
wall13_height);
}
/* Copies the build icons into the window at the bottom. */
void drawbuildicons(void)
{
int i;
if (buildicons) {
/* Blit in the icons */
XSetForeground(display,view_gc,32);
XSetBackground(display,view_gc,63);
for(i=1; i<14; i++)
XCopyPlane(display,wall[i],view_pm,view_gc,0,0,wall1_width,
wall1_height,(i-1)*wall1_width,window_h - wall1_width,1);
}
}
void drawstars(struct map *mp, struct view *vw)
{
float angle, elev;
int i;
angle = atan2(vw->n.x,vw->n.y);
if (angle < 0)
angle += 2*PI;
elev = atan2(vw->n.z,sqrt(vw->n.x*vw->n.x + vw->n.y*vw->n.y));
for(i=0; i<mp->scount; i++) {
float ra, re;
int x,y;
ra = angle - mp->stars[i].bearing;
re = elev - mp->stars[i].elevation;
x = window_w/2 + ra*STARSCALE;
y = window_h/2 + re*STARSCALE;
XSetForeground(display,view_gc,mp->stars[i].intensity + 32);
if (mp->stars[i].radius == 1)
XDrawPoint(display,view_pm,view_gc,x,y);
else {
int r;
r = mp->stars[i].radius;
XFillArc(display,view_pm,view_gc,x-r,y-r,r*2,r*2,0,360*64);
}
}
}